package cn.zhentao.service;

import cn.zhentao.dto.*;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.tea.TeaException;
import com.aliyun.teaopenapi.Client;
import com.aliyun.teaopenapi.models.Config;
import com.aliyun.teaopenapi.models.OpenApiRequest;
import com.aliyun.teaopenapi.models.Params;
import com.aliyun.teautil.models.RuntimeOptions;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 数字人服务
 * 基于阿里云智能体实现数字人通话功能
 *
 * @author zhentao
 */
@Service
@Slf4j
public class DigitalHumanService {

    private final Map<String, Client> clientByRegion = new ConcurrentHashMap<>();

    @Value("${biz.openapi.access.key}")
    private String accessKeyId;

    @Value("${biz.openapi.access.secret}")
    private String accessKeySecret;

    @Value("${biz.digital_human.default_agent_id:default-avatar-agent-id}")
    private String defaultAgentId;

    @Value("${biz.digital_human.default_region:cn-shanghai}")
    private String defaultRegion;

    @Value("${biz.live_mic.app_id}")
    private String liveMicAppId;

    @Value("${biz.live_mic.app_key}")
    private String liveMicAppKey;

    /**
     * 获取指定区域的客户端
     */
    public Client getClient(String region) {
        if (clientByRegion.containsKey(region)) {
            return clientByRegion.get(region);
        }
        // 使用 computeIfAbsent 方法在并发环境下安全地获取或创建 Client
        return clientByRegion.computeIfAbsent(region, this::createClient);
    }

    /**
     * 创建指定区域的客户端
     */
    private Client createClient(String region) {
        try {
            Config config = new Config()
                    // 您的 AccessKey ID
                    .setAccessKeyId(accessKeyId)
                    // 您的 AccessKey Secret
                    .setAccessKeySecret(accessKeySecret);
            // 访问的域名
            config.endpoint = String.format("ice.%s.aliyuncs.com", region);
            return new Client(config);
        } catch (Exception e) {
            log.error("createClient error. e:{}", e.getMessage());
            throw new RuntimeException(e);
        }
    }

    /**
     * 生成数字人通话实例
     */
    public DigitalHumanCallResponseDto generateDigitalHumanCall(DigitalHumanCallRequestDto request) {
        String region = StringUtils.isNotBlank(request.getRegion()) ? request.getRegion() : defaultRegion;
        String aiAgentId = StringUtils.isNotBlank(request.getAiAgentId()) ? request.getAiAgentId() : defaultAgentId;

        Params params = new Params()
                .setAction("GenerateAIAgentCall")
                .setVersion("2020-11-09")
                .setProtocol("HTTPS")
                .setMethod("POST")
                .setAuthType("AK")
                .setStyle("HTTPS")
                .setPathname("/")
                .setReqBodyType("json")
                .setBodyType("json");

        java.util.Map<String, Object> queries = new java.util.HashMap<>();
        queries.put("UserId", request.getUserId());
        queries.put("AIAgentId", aiAgentId);
        queries.put("AgentType", request.getAgentType());
        queries.put("Expire", request.getExpire());

        if (StringUtils.isNotBlank(request.getUserData())) {
            queries.put("UserData", request.getUserData());
        }
        if (StringUtils.isNotBlank(request.getTemplateConfig())) {
            queries.put("TemplateConfig", request.getTemplateConfig());
        }
        if (StringUtils.isNotBlank(request.getAgentConfig())) {
            queries.put("AgentConfig", request.getAgentConfig());
        }
        if (StringUtils.isNotBlank(request.getSessionId())) {
            queries.put("SessionId", request.getSessionId());
        }
        if (StringUtils.isNotBlank(request.getChatSyncConfig())) {
            queries.put("ChatSyncConfig", request.getChatSyncConfig());
        }

        RuntimeOptions runtime = new RuntimeOptions();
        String requestId = StringUtils.EMPTY;
        String message = StringUtils.EMPTY;
        String errCode = StringUtils.EMPTY;
        int code = 500;

        try {
            OpenApiRequest openApiRequest = new OpenApiRequest()
                    .setQuery(com.aliyun.openapiutil.Client.query(queries));
            Client localClient = getClient(region);
            long start = System.currentTimeMillis();
            log.info("generateDigitalHumanCall, queries:{}, region:{}", JSONObject.toJSONString(queries), region);
            Map<String, ?> response = localClient.callApi(params, openApiRequest, runtime);
            log.info("generateDigitalHumanCall, response:{}, cost:{}ms", JSONObject.toJSONString(response), (System.currentTimeMillis() - start));

            if (response != null && response.containsKey("statusCode")) {
                Integer statusCode = (Integer) response.get("statusCode");
                if (200 == statusCode) {
                    Map<String, Object> body = (Map<String, Object>) response.get("body");

                    String instanceId = (String) body.get("InstanceId");
                    String channelId = (String) body.get("ChannelId");
                    String agentUserId = (String) body.get("AIAgentUserId");
                    // 阿里云返回字段为 Token（Base64 JSON），用于RTC鉴权
                    String rtcAuthToken = (String) body.get("Token");
                    requestId = (String) body.get("RequestId");

                    // 获取数字人实例的详细信息（包含RuntimeConfig）
                    DigitalHumanInstanceResponseDto instanceInfo = null;
                    try {
                        DigitalHumanInstanceRequestDto instanceRequest = new DigitalHumanInstanceRequestDto();
                        instanceRequest.setAiAgentInstanceId(instanceId);
                        instanceRequest.setRegion(region);
                        instanceInfo = describeDigitalHumanInstance(instanceRequest);
                        log.info("获取数字人实例详情成功: {}", instanceInfo);
                    } catch (Exception e) {
                        log.warn("获取数字人实例详情失败: {}", e.getMessage());
                    }

                    // 构建响应，包含RuntimeConfig信息
                    DigitalHumanCallResponseDto.DigitalHumanConfig.DigitalHumanConfigBuilder configBuilder = DigitalHumanCallResponseDto.DigitalHumanConfig.builder();

                    if (instanceInfo != null && instanceInfo.getCode() == 200) {
                        // 解析RuntimeConfig
                        try {
                            JSONObject runtimeConfig = JSONObject.parseObject(instanceInfo.getRuntimeConfig());
                            if (runtimeConfig != null) {
                                configBuilder.avatarUrl(runtimeConfig.getString("AvatarUrl"))
                                           .voiceId(runtimeConfig.getString("VoiceId"))
                                           .enableVoiceInterrupt(runtimeConfig.getBoolean("EnableVoiceInterrupt"))
                                           .enablePushToTalk(runtimeConfig.getBoolean("EnablePushToTalk"));
                            }
                        } catch (Exception e) {
                            log.warn("解析RuntimeConfig失败: {}", e.getMessage());
                        }
                    }

                    return DigitalHumanCallResponseDto.builder()
                            .code(200)
                            .message("success")
                            .requestId(requestId)
                            .aiAgentInstanceId(instanceId)
                            .channelId(channelId)
                            .aiAgentUserId(agentUserId)
                            .rtcAuthToken(rtcAuthToken)
                            .agentType(request.getAgentType())
                            .sessionId(request.getSessionId())
                            .digitalHumanConfig(configBuilder.build())
                            .build();
                }
            }
        } catch (TeaException e) {
            log.error("generateDigitalHumanCall Tea error. e:{}", e.getMessage());
            requestId = e.getData().get("RequestId").toString();
            message = e.getMessage();
            code = e.getStatusCode();
            errCode = e.getCode();
        } catch (Exception e) {
            message = e.getMessage();
            log.error("generateDigitalHumanCall error. e:{}", e.getMessage());
        }

        return DigitalHumanCallResponseDto.builder()
                .code(code)
                .message(message)
                .requestId(requestId)
                .errorCode(errCode)
                .build();
    }

    /**
     * 查询数字人实例信息
     */
    public DigitalHumanInstanceResponseDto describeDigitalHumanInstance(DigitalHumanInstanceRequestDto request) {
        String region = StringUtils.isNotBlank(request.getRegion()) ? request.getRegion() : defaultRegion;

        Params params = new Params()
                .setAction("DescribeAIAgentInstance")
                .setVersion("2020-11-09")
                .setProtocol("HTTPS")
                .setMethod("POST")
                .setAuthType("AK")
                .setStyle("HTTPS")
                .setPathname("/")
                .setReqBodyType("json")
                .setBodyType("json");

        java.util.Map<String, Object> queries = new java.util.HashMap<>();
        queries.put("InstanceId", request.getAiAgentInstanceId());

        RuntimeOptions runtime = new RuntimeOptions();
        String requestId = StringUtils.EMPTY;
        String message = StringUtils.EMPTY;
        String errCode = StringUtils.EMPTY;
        int code = 500;

        try {
            OpenApiRequest openApiRequest = new OpenApiRequest()
                    .setQuery(com.aliyun.openapiutil.Client.query(queries));
            Client localClient = getClient(region);
            long start = System.currentTimeMillis();
            log.info("describeDigitalHumanInstance, queries:{}, region:{}", JSONObject.toJSONString(queries), region);
            Map<String, ?> response = localClient.callApi(params, openApiRequest, runtime);
            log.info("describeDigitalHumanInstance, response:{}, cost:{}ms", JSONObject.toJSONString(response), (System.currentTimeMillis() - start));

            if (response != null && response.containsKey("statusCode")) {
                Integer statusCode = (Integer) response.get("statusCode");
                if (200 == statusCode) {
                    Map<String, Object> body = (Map<String, Object>) response.get("body");
                    Map<String, Object> instance = (Map<String, Object>) body.get("Instance");

                    String callLogUrl = (String) instance.get("CallLogUrl");
                    String runtimeConfig = JSONObject.toJSONString(instance.get("RuntimeConfig"));
                    String status = (String) instance.get("Status");
                    String templateConfig = JSONObject.toJSONString(instance.get("TemplateConfig"));
                    String userData = (String) instance.get("UserData");
                    requestId = (String) body.get("RequestId");

                    return DigitalHumanInstanceResponseDto.builder()
                            .code(200)
                            .message("success")
                            .requestId(requestId)
                            .callLogUrl(callLogUrl)
                            .runtimeConfig(runtimeConfig)
                            .status(status)
                            .templateConfig(templateConfig)
                            .userData(userData)
                            .build();
                }
            }
        } catch (TeaException e) {
            log.error("describeDigitalHumanInstance Tea error. e:{}", e.getMessage());
            requestId = e.getData().get("RequestId").toString();
            message = e.getMessage();
            code = e.getStatusCode();
            errCode = e.getCode();
        } catch (Exception e) {
            message = e.getMessage();
            log.error("describeDigitalHumanInstance error. e:{}", e.getMessage());
        }

        return DigitalHumanInstanceResponseDto.builder()
                .code(code)
                .message(message)
                .requestId(requestId)
                .errorCode(errCode)
                .build();
    }

    /**
     * 生成RTC认证Token
     */
    public RtcAuthTokenResponseDto getRtcAuthToken(RtcAuthTokenRequestDto request) {
        try {
            long timestamp = getClientTimestamp();
            String rtcAuthToken = createBase64Token(request.getChannelId(), request.getUserId(), timestamp);

            return RtcAuthTokenResponseDto.builder()
                    .code(200)
                    .message("success")
                    .rtcAuthToken(rtcAuthToken)
                    .timestamp(timestamp)
                    .expireTime(timestamp + 24 * 60 * 60) // 24小时后过期
                    .build();
        } catch (Exception e) {
            log.error("生成RTC认证Token失败: {}", e.getMessage());
            return RtcAuthTokenResponseDto.builder()
                    .code(500)
                    .message("生成RTC认证Token失败: " + e.getMessage())
                    .timestamp(System.currentTimeMillis() / 1000)
                    .build();
        }
    }

    private long getClientTimestamp() {
        java.util.Calendar calendar = java.util.Calendar.getInstance();
        calendar.add(java.util.Calendar.DAY_OF_MONTH, 1);
        return calendar.getTime().getTime() / 1000;
    }

    private String createBase64Token(String channelId, String userId, long timestamp) {
        String rtcAuthStr = String.format("%s%s%s%s%d", liveMicAppId, liveMicAppKey, channelId, userId, timestamp);
        String rtcAuth = sha256(rtcAuthStr);

        // 手动构建JSON字符串
        String tokenJson = String.format(
            "{\"appid\":\"%s\",\"channelid\":\"%s\",\"userid\":\"%s\",\"nonce\":\"\",\"timestamp\":%d,\"token\":\"%s\"}",
            liveMicAppId, channelId, userId, timestamp, rtcAuth
        );

        return java.util.Base64.getEncoder().encodeToString(tokenJson.getBytes(java.nio.charset.StandardCharsets.UTF_8));
    }

    /**
     * SHA256签名
     */
    private static String sha256(String input) {
        try {
            java.security.MessageDigest digest = java.security.MessageDigest.getInstance("SHA-256");
            byte[] hash = digest.digest(input.getBytes(java.nio.charset.StandardCharsets.UTF_8));
            StringBuilder hexString = new StringBuilder();
            for (byte b : hash) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1) {
                    hexString.append('0');
                }
                hexString.append(hex);
            }
            return hexString.toString();
        } catch (java.security.NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }
}
